Cette animation montre l’évolution de l’application depuis le premier commit jusqu’au dernier.

Nous partons d’une représentation assez basique (poser les bases) vers l’ajout progressif d’éléments d’interaction utilisateur, de représentation de la données, ainsi que d’éléments contextuels textes.

Préambule

Pour créer une application Shiny, il faut, depuis RStudio, aller dans File > New File > Shiny Web App...

Cela crée une application basique qui sert d’exemple, avec une liste déroulante et un graphique d’exemple :

library(shiny)

ui <- fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"), # on changera le nom de l'appli plus tard

    sidebarLayout(
        sidebarPanel(
            "liste"
        ),
        mainPanel(
          "résultats"
        )
    )
)

server <- function(input, output) {
  # vide pour le moment
}

# Run the application 
shinyApp(ui = ui, server = server)
  • ui est la partie destinée à accueillir les éléments d’interface (esthétique)
  • server est la partie dans laquelle seront définis les mécanismes d’interaction, d’interrogation et de représentation des données.

En quelque sorte, par analogie avec un modèle MVC (Modèle Vue Contrôleur), ui concerne la Vue et server le Contrôleur.

Voir cette version

Pose les bases

Dans un premier temps, on pose les bases, et on efface certains éléments de l’application d’exemple.

Voir cette version

server.R

La partie est laissée vide pour le moment :

server <- function(input, output) {
# vide
}

Lecture des données et liste déroulante des communes

On lit les données de flux :

flux <- read_csv("data/obs_artif_conso_com_2009_2020_V2.csv", na = c("", "NULL")) %>% 
    filter(idreg == "93")

Ainsi que les données communales :

# Contours de communes
comms <<- readRDS("data/comms.rds") %>% 
    filter(INSEE_REG == 93)

# Liste communes
communes <- flux$idcom
names(communes) <- glue("{flux$idcomtxt} ({flux$idcom})")

ui.R

On ajoute la liste déroulante des communes maintenant qu’elles ont été lues :

    sidebarLayout(
        sidebarPanel(
            selectInput("communes", label = NULL, choices = communes, selected = NULL),
        ),

        mainPanel(
           "Résultats"
        )
    )

Voir cette version

Affiche un tableau des résultats

On peut afficher un tableau des résultats de flux pour une commune sélectionnée, juste pour tester les mécanismes de sélection, notamment la fonction getStatsFlux vue dans un précédent notebook.

ui.R

mainPanel(
          dataTableOutput("tbResults")
        )

server.R

On ajoute les résultats avec renderDataTable et dataTableOutput :

    output$tbResults <- renderDataTable({
        codeInsee <- input$communes
        df <- flux %>% getStatsFlux(codeInsee)
        return(df)
    })

Voir cette version

Ajoute le stream

On ajoute le streamgraph.

ui.R

mainPanel(
            streamgraphOutput("streamPlot")
        )

server.R

    output$streamPlot <- renderStreamgraph({
        myStream <- flux %>% makeStream(codeInsee)
        return(myStream)
    })

Voir cette version

Ajoute les infos communales

On peut ajouter des informations assez sommaires sur la commune sélectionnée, sous la forme de texte.

ui.R

mainPanel(
    textOutput("txtCommune"),
    streamgraphOutput("streamPlot")
)

server.R

output$txtCommune <- renderText({
    codeInsee <- input$communes
    fComm <- flux %>% filter(idcom == codeInsee)

    paste(fComm$idcomtxt, fComm$idcom, fComm$artcom0920)
})

Voir cette version

Ajoute la légende

Pour la légende, on utilise simplement de l’HTML stylisé, plutôt que la légende du plot :

tagList(
            div(
                myStream,
                style="margin-bottom:20px;"
            ),
            div(
                tags$span("Habitat",
                          style = glue("background-color:{myPalette['blue']};padding:10px;")),
                tags$span("Activité",
                          style = glue("background-color:{myPalette['red']};padding:10px;")),
                tags$span("Mixte",
                          style = glue("background-color:{myPalette['magenta']};padding:10px;")),
                tags$span("Inconnu",
                          style = glue("background-color:{myPalette['grey']};padding:10px;")),
                style="text-align:center"
            )
        )

On peut faire du HTML assez avancé avec R Shiny !

Voir cette version

Titre avec couleurs cerema

Ici, on intègre un élément de charte graphique Cerema, notamment la palette graphique de l’établissement au format JSON.

Voir cette version

Ajoute la surface artificialisée totale

On ajoute une nouvelle information en texte.

Voir cette version

Ajout de reactive fComm

Les variables de type reactive sont très utiles lorsqu’il s’agit de récupérer à plusieurs endroits le résultat d’une variable calculée car cela évite, entre autres, de devoir répliquer les mécanismes de création de cette variable à ces multiples endroits. Aussi, un mécanisme interne permet de ne pas recalculer la variable si elle n’a pas changé avec les nouveaux critères de création.

server.R

fComm <- reactive({
    fComm <- flux %>% filter(idcom == input$communes)
    return(fComm)
})

On peut récupérer les variables comme ceci :

fComm()$artcom0920
fComm()$nafart0920

Voir cette version

Ajoute la carte leaflet

ui.R

leafletOutput("mymap")

server.R

output$mymap <- renderLeaflet({
        leaflet() %>%
            addTiles(group = "OSM")
})

Voir cette version

Centre la carte sur PACA

Pour ajuster la carte sur PACA, on utile la fonction fitBounds :

output$mymap <- renderLeaflet({

        bb <- st_bbox(comms) %>% as.numeric

        leaflet() %>%
            addTiles(group = "OSM") %>%
            fitBounds(lng1 = bb[1], 
                      lat1 = bb[2], 
                      lng2 = bb[3], 
                      lat2 = bb[4])
    })

Voir cette version

Ajoute les fonds de carte

Ajoutons davantage de fonds cartos, notamment ceux de l’IGN : orthophoto, Plan IGN.

server.R

output$mymap <- renderLeaflet({

    leaflet() %>%
            addTiles(group = "OSM") %>%
            addProviderTiles(providers$CartoDB.PositronOnlyLabels, group = "Villes") %>% 
            addTiles("http://wxs.ign.fr/choisirgeoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE=normal&TILEMATRIXSET=PM&FORMAT=image/png&LAYER=GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}",
                     options = c(WMSTileOptions(tileSize = 256),
                                 providerTileOptions(minZoom = 1, maxZoom = 15)),
                     attribution='<a target="_blank" href="https://www.geoportail.gouv.fr/">Geoportail France</a>',
                     group = "Plan IGN"
            ) %>%
            addTiles("http://wxs.ign.fr/choisirgeoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE=normal&TILEMATRIXSET=PM&FORMAT=image/jpeg&LAYER=ORTHOIMAGERY.ORTHOPHOTOS&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}",
                     options = c(WMSTileOptions(tileSize = 256),
                                 providerTileOptions(minZoom = 1, maxZoom = 22)),
                     attribution='<a target="_blank" href="https://www.geoportail.gouv.fr/">Geoportail France</a>',
                     group = "Photo aérienne"
            ) %>%
            addLayersControl(baseGroups    = c("Photo aérienne", "Plan IGN", "OSM"),
                             overlayGroups = "Villes",
                             options       = layersControlOptions(collapsed = FALSE)) %>%
            fitBounds(lng1 = bb[1], lat1 = bb[2], lng2 = bb[3], lat2 = bb[4])
})

Voir cette version

Coordonnées au clic

Pour trouver les coordonnées au clic, on utile un mécanisme de sélection basé sur input, notamment input$mymap.

A noter que l’on peut aussi réagir au clic sur une entité (input$mymap_shape_click), voire au passage (input$mymap_shape_mouseover). Voir à ce titre la page dédiée sur R Leaflet.

ui.R

verbatimTextOutput("foo")

server.R

output$foo <- renderPrint({
    input$mymap_click
})

Voir cette version

Rend codeInsee reactive

Le code INSEE est récupéré dynamiquement après un clic sur la carte.

On rend le code INSEE reactive car il sera utilisé à plusieurs endroits : ici ou encore

Voir cette version

Ajoute le reactive

On peut même créer des reactive en cascade, comme ici, pour fComm() qui s’appuie sur codeInsee()

server.R

fComm <- reactive({
        fComm <- flux %>% filter(idcom == input$communes)
        fComm <- flux %>% filter(idcom == codeInsee())
        return(fComm)
    })

Pour le streamgraph, on utilise codeInsee() comme ceci :

myStream <- flux %>% makeStream(codeInsee())

Voir la ligne de code

Voir cette version

Fix : ajoute req

Quand les données ne sont pas encore chargées, on peut avoir un rendu bizarre avec un message d’erreur.

Du coup, on introduit la fonction req (pour ‘requiert’) qui exécute la suite seulement si la condition est réalisée :

fComm <- reactive({
    req(codeInsee()) # < ici

    fComm <- flux %>% filter(idcom == codeInsee())
    return(fComm)
})

Voir cette version

Ajoute un marqueur

On crée un proxy à la carte pour réaliser plus tard des actions sur celle-ci (zoomer, ajouter des éléments cartos) sans avoir à recalculer la carte (appelée mymap) à chaque fois :

proxy <- leafletProxy("mymap")

On ajoute le centroïde de la commune cliquée :

observe({
    req(codeInsee())
    myComm <- comms %>% filter(INSEE_COM == codeInsee())

    # Ajout du marqueur
    proxy %>% 
        clearMarkers() %>% 
        addMarkers(data = myComm %>% st_centroid)
})

Voir cette version

Ajoute le contour de la commune

On ajoute le contour de la commune (c’est plus sympa) :

proxy %>% 
    clearShapes() %>% 
    addPolygons(data = myComm,
                color = paletteCerema$secondaire$orange, 
                weight = 1, 
                smoothFactor = 0.5,
                opacity = 1, 
                fillOpacity = 0.3,
                fillColor = paletteCerema$secondaire$orange,
                highlightOptions = highlightOptions(color = paletteCerema$secondaire$orange, 
                                                    weight = 2,
                                                    fillOpacity = 0.1,
                                                    bringToFront = TRUE))

Voir cette version

Va vers la commune

Le marqueur s’affiche, le contour aussi, mais parfois la commune semble loin, lo i n, l o i n

Du coup, petite astuce ergonomique : au clic, on va automatiquement vers la commune cliquée :

bb <- st_bbox(myComm)
proxy %>%
    flyToBounds(lng1 = as.numeric(bb$xmin),
                lat1 = as.numeric(bb$ymin),
                lng2 = as.numeric(bb$xmax),
                lat2 = as.numeric(bb$ymax))

Voir cette version

La liste déroulante permet d’aller vers une commune

On peut même changer le comportement de la liste déroulante utilisée au début pour afficher les stats (voir cette ligne) pour aller automatiquement vers la commune cliquée :

observeEvent(input$communes, {
    codeInsee <- input$communes
    bb <- comms %>% filter(INSEE_COM == codeInsee) %>% st_bbox %>% as.numeric
    proxy %>% flyToBounds(lng1 = bb[1], lat1 = bb[2], lng2 = bb[3], lat2 = bb[4])
})

C’est aussi l’occasion d’introduire observeEvent qui ‘écoute’ les évènements, et qui, ici déclenche une action lorsqu’on choisit un élément dans la liste des communes (input$communes)

Voir cette version

Ajoute l’élément vide à la liste des communes

Généralement, le premier élément d’une liste est une instruction type ‘Veuillez choisir une commune’

Voir cette version

Précise qu’il faut cliquer sur la carte pour afficher les stats

Au lieu d’utiliser req (voir ce commit) qui conditionne l’affichage à la sélection d’une commune, nous pouvons générer un message pour préciser qu’il faut cliquer sur la carte lorsqu’aucune commune n’a été cliquée.

Si aucune commune n’est sélectionnée : valeur nulle, alors on affiche ce message (Voir la ligne de code) :

if(is.null(codeInsee())) return(tagList(icon("mouse-pointer"), "Cliquez sur la carte pour afficher les statistiques"))

Voir cette version

Ajoute la treemap et supprime la légende

Tiens, comme on y est, pourquoi pas ajouter une treemap.

ui.R

plotlyOutput("treemap")

server.R

output$treemap <- renderPlotly({
    req(codeInsee())
    flux %>% makeTreemap(codeInsee())  
})

Voir cette version

Dimensions d’une dataviz

Voici selon moi les dimensions d’une dataviz :

Aussi, comme nous l’avons vu dans les notebooks 1 à 7, beaucoup de préparation est nécessaire avant d’aboutir à l’application : préparation des données, voire aussi préparation des mécanismes (fonctions) d’interrogation des données ou de représentation de celles-ci.

Les étapes de développement d’une dataviz

Le développement de cette dataviz a suivi ces différentes étapes :

Collecte

Bien sûr, on ne peut pas représenter des choses qui n’existent pas, d’où l’importance de la collecte, et de la stabilité du point d’accès à la donnée, notamment lorsque votre dataviz doit être mise à jour régulièrement.

Qualité

Une dataviz faite sur des données de mauvaise qualité produira des graphiques de mauvaise qualité.

Sémiologie

Une certaine sémiologie graphique et rigueur doit être suivie afin de restituer de manière convenable l’information à l’utilisateur.

Expérience utilisateur

L’expérience utilisateur doit être prise en compte lors du développement, que ce soit dans la manipulation de l’interface ou la lecture des graphiques.

Fonctionnalité

Rendre fonctionnel et généralisable les mécanismes sous la forme de fonctions permet déjà de rendre le code plus parlant, plus léger à la lecture, plus pérenne et appropriable par une tierce personne, mais aussi de les utiliser ailleurs, dans d’autres applis.

Esthétique

Pas mal de temps peut être passé dans l’esthétique. Le respect d’une charte graphique, l’esthétisation est souvent le premier désir de celui qui vous demandera une application de dataviz.

Refactoring

Enfin, le refactoring vise à améliorer les commentaires dans le code, réorganiser certaines fonctions, certains blocs de code pour faciliter la lecture et la pérénnité du code informatique, son appropriation par d’autres, ou par vous-même (lorsque le vent vous aura mené entre temps vers de multiples applis et que vous l’aurez un peu oubliée !)